package pl.maciejziarko.mshare.security;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.SimpleByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import pl.maciejziarko.mshare.domain.Account;
import pl.maciejziarko.mshare.domain.Role;
import pl.maciejziarko.mshare.service.AccountService;
import pl.maciejziarko.mshare.service.RoleService;
import pl.maciejziarko.mshare.service.exception.AccountNotFoundException;
import pl.maciejziarko.mshare.service.exception.AccountServiceException;
import pl.maciejziarko.mshare.service.exception.RoleServiceException;

import java.util.List;

/**
 * @author Maciej Ziarko
 */
public final class MShareRealm extends AuthorizingRealm {

    private static final Logger LOG = LoggerFactory.getLogger(MShareRealm.class);

    private AccountService accountService;
    private RoleService roleService;

    @Autowired
    public MShareRealm(AccountService accountService, RoleService roleService) {
        this.accountService = accountService;
        this.roleService = roleService;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // Retrieving principal's username:
        String username = (String) principals.getPrimaryPrincipal();

        Account account = new Account(username);

        List<Role> roles = null;
        try {
            roles = roleService.getRoles(account);
        } catch (RoleServiceException e) {
            throw new AccountException(e.getMessage());
        }

        return new MShareAuthorizationInfo(roles);
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        LOG.debug("doGetAuthenticationInfo");
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();

        if (username == null) {
            throw new AccountException("Null usernames are not allowed by this realm.");
        }

        Account account = null;
        try {
            account = accountService.getCredentials(username);
        } catch (AccountNotFoundException e) {
            LOG.debug("Unknown account: [{}]", username);
            throw new UnknownAccountException("No account found for user [" + username + "]");
        } catch (AccountServiceException e) {
            throw new AccountException();
        }
        Sha256Hash hash = new Sha256Hash(((UsernamePasswordToken) token).getPassword(), account.getSalt());
        LOG.debug("HASH:{}", hash.toHex());
        Sha256Hash hashedPassword = Sha256Hash.fromHexString(account.getHashedPassword());
        hashedPassword.setSalt(new SimpleByteSource(account.getSalt()));

        return new SimpleAuthenticationInfo(username, hashedPassword, getName());
    }
}
